package com.nulldev.util.graphics.renderIt.renderer.swing.nitrous;

import java.awt.*;
import java.awt.peer.ComponentPeer;

import com.nulldev.util.JVM.Arguments;
import com.nulldev.util.JVM.JVM;
import com.nulldev.util.gui.swingAPI.Swing;
import com.nulldev.util.osAPI.OS;

/**
 * The interface for a renderer.
 * <p/>
 * A renderer is responsible for producing a {@link java.awt.Graphics2D} to draw
 * on and switching the {@link java.awt.Panel} to render with it.
 *
 * @author Quantum
 * @author Tudor
 */
public interface IRenderManager {

	static final boolean ENABLE_XRENDER = Arguments.notArguments("--Nitrous.disableXRender");
	static final boolean ALLOW_D3D = System.getenv().getOrDefault("J2D_D3D_NO_HWCHECK", "false").equalsIgnoreCase("true");
	static final boolean ENABLE_DEBUGGING = Arguments.hasArgument("--Nitrous.debug");

	/**
	 * The {@link java.lang.Class} objects for each available renderer.
	 */
	@SuppressWarnings("unchecked")
	public static Class<? extends IRenderManager>[] RENDERERS = new Class[]
		{ D3DRenderManager.class, // Followed by D3D (on most cards)
				GDIRenderManager.class, // GDI is fastest for drawing just one image
				WGLRenderManager.class, // Some cards may do GL better than D3D, but this is unlikely
				XRenderManager.class, // Linux XRender/X11!
		};

	public static Class<? extends IRenderManager> getPreferredClass() {
		if (OS.isLinux() || OS.isBSD())
			return XRenderManager.class;
		if (OS.isWindows()) {
			return D3DRenderManager.class;
		}
		return GDIRenderManager.class;
	}

	public static IRenderManager tryFor(final java.awt.Panel peer) {
		try {
			final Object cmp = Swing.getPeer(peer);
			for (final Class<? extends IRenderManager> clazz : RENDERERS) {
				if (clazz == XRenderManager.class && !ENABLE_XRENDER)
					continue;
				if (clazz == D3DRenderManager.class && !ALLOW_D3D)
					continue;
				try {
					return clazz.getDeclaredConstructor(ComponentPeer.class).newInstance(cmp);
				} catch (Exception f) {
					if (!(f instanceof ClassNotFoundException) && ENABLE_DEBUGGING)
						JVM.errorln(f);
					continue;
				}
			}
			return FallbackRenderManager.class.getDeclaredConstructor(ComponentPeer.class).newInstance(cmp);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public static IRenderManager tryFor(final ComponentPeer peer) {
		try {
			for (final Class<? extends IRenderManager> clazz : RENDERERS) {
				if (clazz == XRenderManager.class && !ENABLE_XRENDER)
					continue;
				if (clazz == D3DRenderManager.class && !ALLOW_D3D)
					continue;
				try {
					return clazz.getDeclaredConstructor(ComponentPeer.class).newInstance(peer);
				} catch (Exception f) {
					if (!(f instanceof ClassNotFoundException) && ENABLE_DEBUGGING)
						JVM.errorln(f);
					continue;
				}
			}
			return FallbackRenderManager.class.getDeclaredConstructor(ComponentPeer.class).newInstance(peer);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * Called to produce a {@link java.awt.Graphics2D} for rendering.
	 *
	 * @return a {@link java.awt.Graphics2D} to render on.
	 */
	Graphics2D getGraphics();

	/**
	 * Called when switching to this renderer.
	 * <p/>
	 * This should set up our {@link java.awt.Panel} to make use of this renderer,
	 * among any other setup code this renderer needs.
	 */
	void update();
}